import * as BABYLON from '@babylonjs/core';
import { SceneManager } from './SceneManager';
import { SignalRService } from '../services/SignalRService';
import { AppStoreService } from '../services/AppStoreService';
import { HttpService } from '../services/HttpService';
import { JsonHelper } from '../JsonHelper';
import { KrData } from './KrData';
import { TTelegram, TWcsScene, TWcsSceneDeviceInfo, TWcsScenePermission } from '../models';

export class BabylonCanvas {
  public engine: BABYLON.Engine;
  public scenes: Map<number, BABYLON.Scene> = new Map<number, BABYLON.Scene>();
  public backgroundColor?: string;
  public backgroundImage?: string;
  /**
   * 定义了画布 HTML 节点。
   */
  public canvasElement: HTMLCanvasElement;
  protected _tWcsScenes: TWcsScene[];
  protected _infoColors: TWcsSceneDeviceInfo[];
  protected _permissions: TWcsScenePermission[];
  private _hub: SignalRService;
  private _sceneManger: SceneManager;
  
  /**
   * 初始化 @see BabylonCanvas 类的新实例。
   * @param {HTMLCanvasElement} canvasElement 画布 HTML 节点。
   * @param {TWcsSceneMesh[]} tWcsScenes 布局数据。
   * @param {TWcsSceneDeviceInfo[]} infoColors 信息颜色数据。
   * @param {TWcsScenePermission[]} permissions 布局角色权限。
   */
  protected constructor(
    canvasElement: HTMLCanvasElement,
    tWcsScenes: TWcsScene[],
    infoColors: TWcsSceneDeviceInfo[],
    permissions: TWcsScenePermission[],
  ) {
    this.canvasElement = canvasElement;
    this._tWcsScenes = tWcsScenes;
    this._infoColors = infoColors;
    this._permissions = permissions;

    this.engine = new BABYLON.Engine(this.canvasElement);
    this._sceneManger = new SceneManager(this, this._tWcsScenes);
    window.addEventListener('resize', () => {
      this.engine.resize();
    });

    var url = new URL('http://192.168.1.100:8000').href;
    this._hub = new SignalRService(url);
    this._hub.on('serverRequest', this.onServerRequest);
    // this.hub.start();
  }

  public static async create(el: HTMLCanvasElement): Promise<BabylonCanvas> {
    var roles = AppStoreService.getAppStore()
      .getRoles?.map((t) => t.roleId)
      .join(',');
    var pms = await BabylonCanvas.query(TWcsScenePermission, 'isUsed=T && roleId in (' + roles + ')');
    var ics = await BabylonCanvas.query(TWcsSceneDeviceInfo);
    var scs = await BabylonCanvas.query(TWcsScene);
    var canvas = new BabylonCanvas(el, scs, ics, pms);
    await canvas._sceneManger.init();

    // // 测试保存为babylon文件
    // var sceneSerializer = BABYLON.SceneSerializer.Serialize(canvas._sceneManger.getActivedScene()!.scene);
    // const blob = new Blob([JSON.stringify(sceneSerializer)], { type: "application/json" });
    // const url = URL.createObjectURL(blob);
    // const a = document.createElement("a");
    // a.href = url;
    // a.download = "scene.babylon";
    // a.click();
    // URL.revokeObjectURL(url);

    return canvas;
  }

  public getTWcsSceneObjectInfoColors(bmid: string): TWcsSceneDeviceInfo[] {
    return this._infoColors.filter((t) => t.brandModelId == bmid);
  }

  public startRender(): void {
    this.engine.runRenderLoop(() => {
      var activedScene = this._sceneManger.getActivedScene();
      if (activedScene) {
        for(var device of activedScene.device2ds) {
          device.render();
        }
        activedScene.scene.render();
      } else {
        this.engine.clear(new BABYLON.Color4(), true, true, true);
        // 这里是否需要增加一个提示说当前没有场景了？
      }
    });
  }

  public stopRender() {
    this.engine.stopRenderLoop();
  }

  protected static async query<T>(cls: new () => T, where: string = 'isUsed=T'): Promise<T[]> {
    var types = 'kr.service.wcs.data.entity.' + cls.name;
    var result = await HttpService.requestQuery({ types, where });
    var data = result.Data?.get(types) as T[];
    return data;
  }

  private onServerRequest(message: string): void {
    var telegram = JsonHelper.toJson<TTelegram>(message);
    var functionId = telegram.functionId;
    var data = telegram.data;
    if (!functionId) {
      return;
    }

    switch (functionId.toLowerCase()) {
      case 'deviceinfo':
        this.deviceUpdate(data);
        break;
      case 'conninfo':
        this.connectionUpdate(data);
        break;
    }
  }

  private deviceUpdate(data?: KrData): void {}
  private connectionUpdate(data?: KrData): void {}
}
